﻿using AOT;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using UnityEngine.XR;

class IL2CppBridge : IBridge
{
    IntPtr libHandle;
    delegate void deleMonoInit(IntPtr functGetBridge, ILBridge.EnvTag EnvTag);
    delegate void deleMonoEvent(Int64 msgid, Int64 p1, IntPtr p2);
    delegate void deleMonoUpdate(float totaltime, float deltatime);
    deleMonoInit funcMonoInit_Cpp;
    deleMonoEvent funcMonoEvent_Cpp;
    deleMonoUpdate funcMonoUpdate_Cpp;

    static Dictionary<string, IntPtr> exportIL2CPPMethods = null;
    static IntPtr exportGetBridgeFunc;

    public bool CPPDLL_Load(string dllname)
    {
        ILBridge.Log("IL2CppBridge::CPPDLL_Load");

        if (libHandle != IntPtr.Zero)
        {
            ILBridge.Log("::already init MonoBridge");
            return false;
        }

        if (exportGetBridgeFunc == IntPtr.Zero)
        {
            ILBridge.Log("CPPDLL_Load 001");

            Func<IntPtr, IntPtr> _Mono_GetBridgeFunc_Unsafe = IL2CPP_GetBridgeFunc;

            exportGetBridgeFunc = Marshal.GetFunctionPointerForDelegate(_Mono_GetBridgeFunc_Unsafe);


            exportIL2CPPMethods = new Dictionary<string, IntPtr>();
            //准备接口


            {
                //Test 接口
                Action _Mono_Test_Unsafe = Export_TestFunc;
                exportIL2CPPMethods["test_call"] = Marshal.GetFunctionPointerForDelegate(_Mono_Test_Unsafe);
                //Action<IntPtr> _Mono_TestObj_Unsafe = Export_TestFunc_Obj;
                //exportIL2CPPMethods["test_dumpobj"] = Marshal.GetFunctionPointerForDelegate(_Mono_TestObj_Unsafe);
                //Action<IntPtr> _Mono_TestType_Unsafe = Export_TestFunc_Type;
                //exportIL2CPPMethods["test_dumptype"] = Marshal.GetFunctionPointerForDelegate(_Mono_TestType_Unsafe);
                Action<IntPtr, Int32> _Mono_LogString = Export_LogString;
                exportIL2CPPMethods["test_logstring"] = Marshal.GetFunctionPointerForDelegate(_Mono_LogString);
            }
            //补充接口，为了改善IL2CPP调用而增加
            //经测试从c#一侧很难操作，IL2CPP环境操作尽量从c++侧进行
            //{
            //    {
            //        Func<IntPtr, IntPtr, IntPtr> _il2cppex_GetType = Export_IL2CPPEx_GetType;
            //        exportIL2CPPMethods["il2cppex_gettype_handle"] = Marshal.GetFunctionPointerForDelegate(_il2cppex_GetType);
            //    }
            //    {
            //        Func<IntPtr, IntPtr, IntPtr> _il2cppex_callback2delegate = Export_IL2CPPEx_Callback2Delegate;
            //        exportIL2CPPMethods["il2cppex_callback2delegate_handle"] = Marshal.GetFunctionPointerForDelegate(_il2cppex_callback2delegate);
            //    }
            //}
        }
        ILBridge.Log("CPPDLL_Load 002");

        libHandle = libShell.LoadModule(dllname);
        ILBridge.Log("loadlib=" + libHandle.ToString("X16"));
        if (libHandle == null)
        {
            var str = libShell.GetLoadModuleError();
            ILBridge.Log("loaderror:" + str);
            return false;
        }
        IntPtr funchandleInit = libShell.GetSymbol(libHandle, "cpp_Init");
        IntPtr funchandleEvent = libShell.GetSymbol(libHandle, "cpp_Event");
        IntPtr funchandleUpdate = libShell.GetSymbol(libHandle, "cpp_Update");
        ILBridge.Log("funchandleInit=" + funchandleInit.ToString("X16"));
        ILBridge.Log("funchandleEvent=" + funchandleEvent.ToString("X16"));
        ILBridge.Log("funchandleUpdate=" + funchandleUpdate.ToString("X16"));

        funcMonoInit_Cpp = (deleMonoInit)Marshal.GetDelegateForFunctionPointer(funchandleInit, typeof(deleMonoInit));
        funcMonoEvent_Cpp = (deleMonoEvent)Marshal.GetDelegateForFunctionPointer(funchandleEvent, typeof(deleMonoEvent));
        funcMonoUpdate_Cpp = (deleMonoUpdate)Marshal.GetDelegateForFunctionPointer(funchandleUpdate, typeof(deleMonoUpdate));


        try
        {
            funcMonoInit_Cpp(exportGetBridgeFunc, ILBridge.EnvTag.IL2CPP);
        }
        catch (Exception err)
        {
            ILBridge.Log("CPPDLL_Event Error" + err.Message);
        }
        return true;

    }

    public void CPPDLL_Event(Int64 msgid, Int64 param1, IntPtr param2)
    {
        try
        {
            if (libHandle != IntPtr.Zero)
            {
                funcMonoEvent_Cpp(msgid, param1, param2);
            }
        }
        catch (Exception err)
        {
            ILBridge.Log("CPPDLL_Event Error" + err.Message);
        }
    }

    public void CPPDLL_CallUpdate()
    {
        try
        {
            if (libHandle != IntPtr.Zero)
            {
                funcMonoUpdate_Cpp(UnityEngine.Time.time, UnityEngine.Time.deltaTime);
            }
        }
        catch (Exception err)
        {
            ILBridge.Log("CPPDLL_Event Error" + err.Message);
        }
    }
    public void CPPDLL_Free()
    {
        if (libHandle != IntPtr.Zero)
        {
            libShell.FreeModule(libHandle);
            libHandle = IntPtr.Zero;
            funcMonoInit_Cpp = null;
            funcMonoEvent_Cpp = null;
            funcMonoUpdate_Cpp = null;
        }
    }
    [MonoPInvokeCallback(typeof(Func<IntPtr, IntPtr>))]
    static IntPtr IL2CPP_GetBridgeFunc(IntPtr unsafeCharPtr)
    {
        IntPtr handle = libShell.GetSymbolUnsafe(libShell.GetRuntimeModule(), unsafeCharPtr);
        if (handle != IntPtr.Zero)
        {
            return handle;
        }
        if (handle == IntPtr.Zero)
        {
            //自己导出几个测试用的
            string name = Marshal.PtrToStringAnsi(unsafeCharPtr);

            if (IL2CppBridge.exportIL2CPPMethods.TryGetValue(name, out handle))
            {
                return handle;
            }
            UnityEngine.Debug.LogWarning("IL2CPP_GetBridgeFunc not found : " + name);
        }
        return IntPtr.Zero;

    }

    [MonoPInvokeCallback(typeof(Action<IntPtr, int>))]
    static void Export_LogString(IntPtr unsafeCharPtr, int level)
    {
        string str = Marshal.PtrToStringAnsi(unsafeCharPtr);
        ILBridge.Log(str, (ILBridge.LogLevel)level);
    }
    [MonoPInvokeCallback(typeof(Action))]
    static void Export_TestFunc()
    {
        //native wrap 的两端是隔离的,所有的Export内部应该有try，防止把错误传递到Native
        try
        {
            ILBridge.Log("Mono_TestFunc. call from cpp");
        }
        catch (Exception err)
        {
            //这里err.StackTrace无法穿透NativeWrap,但是Log本身的调用记录可以
            UnityEngine.Debug.LogError(err.Message + "|" + err.StackTrace + "|");
        }
    }
    //[MonoPInvokeCallback(typeof(Action<IntPtr>))]
    //static void Export_TestFunc_Obj(IntPtr handle)
    //{
    //    try
    //    {
    //        UnityEngine.Debug.LogWarning("Mono_TestFunc_Obj.");
    //        var h = GCHandle.FromIntPtr(handle);
    //        UnityEngine.Debug.LogWarning(h.Target);
    //        if (h.Target != null)
    //        {
    //            UnityEngine.Debug.LogWarning(h.Target.GetType());
    //        }
    //    }
    //    catch (Exception err)
    //    {
    //        //这里err.StackTrace无法穿透NativeWrap,但是Log本身的调用记录可以
    //        UnityEngine.Debug.LogError(err.Message + "|" + err.StackTrace + "|");
    //    }
    //}
    //[MonoPInvokeCallback(typeof(Action<IntPtr>))]

    //static void Export_TestFunc_Type(IntPtr handle)
    //{
    //    try
    //    {
    //        UnityEngine.Debug.LogWarning("type=" + handle);
    //    }
    //    catch (Exception err)
    //    {
    //        //这里err.StackTrace无法穿透NativeWrap,但是Log本身的调用记录可以
    //        UnityEngine.Debug.LogError(err.Message + "|" + err.StackTrace + "|");
    //    }
    //}
}
